//@brief

#include <rte_abi.h>
#include "protocol.h"
#include "string.h"

int protocol::fill_byte(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    const u8 *inx;
    inx = elt->context;
    buf_data_put(&tx_buf, inx, elt->len);
    return elt->len;
}

int protocol::fill_byte_parent(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    return buf_data_put(&tx_buf, (const u8 *) prl->context, elt->len);
}

#ifdef QT
#include <QtCore/QTime>
#include <QtCore/QCoreApplication>
void delay_ms(int ms)
{
    QTime t;
    t.start();
    while (t.elapsed() < ms)
        QCoreApplication::processEvents();
}
#endif

int protocol::select(u32 timeout) {
    u32 tmo = plc_rte->timestamp() + timeout;
    while (plc_rte->timestamp() < tmo) {
        if (plc_rte->SerialAvailable(serialNo))
            return 1;
        plc_rte->delay(1);
    }
    return 0;
}

//,
int protocol::read_head(u8 len, const u8 *context, u32 timeout) {
    int i;
    u32 tmo = plc_rte->timestamp() + timeout;
    while (plc_rte->timestamp() < tmo) {
        if (plc_rte->SerialAvailable(serialNo)) {
            *(rx_buf.winx) = (u8)plc_rte->SerialRead(serialNo);
            rx_buf.winx++;
            if (len == buf_data_len(&rx_buf)) {
                i = memcmp((const char *) context, (const char *) rx_buf.rinx, len);
                if (i) {
                    return -2;
                }
                return buf_data_len(&rx_buf);
            }
        } else
            plc_rte->delay(1);
    }
    return -1;
}

int protocol::read_until(const u8 *context, u8 conetxt_len, u32 timeout) {
    int s_rc = 0, i;
    while (true) {

        s_rc = plc_rte->SerialAvailable(serialNo);
        if (s_rc) {

            *rx_buf.winx = (u8)plc_rte->SerialRead(serialNo);
            rx_buf.winx++;
            conetxt_len = (u8) buf_data_len(&rx_buf);
            i = memcmp((const char *) context, (const char *) rx_buf.winx, conetxt_len);
            rx_buf.winx += s_rc;
            if (i == 0) {
                return buf_data_len(&rx_buf); //
            }
        } else
            return -2;
    }
    return -1; //
}

int protocol::elt_read_head(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    return read_head(elt->len, elt->context, interval);
}

int protocol::read_cmp_context(u8 len, const u8 *context, u32 timeout) {
    int rxlen;
    u8 *inx = rx_buf.winx;
    u32 tmo = plc_rte->timestamp() + timeout;
    rxlen = 0;
    while (plc_rte->timestamp() < tmo) {
        if (plc_rte->SerialAvailable(serialNo)) {

            *rx_buf.winx = (u8) plc_rte->SerialRead(serialNo);
            rx_buf.winx++;
            rxlen++;
            if (len == rxlen) {
                if (strncmp((const char *) context, (const char *) inx, len)) {
                    return -3; //
                }
                return len; //
            }
        } else
            plc_rte->delay(1);
    }
    return -1; //
}

int protocol::elt_read_cmp_context(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    return read_cmp_context(elt->len, elt->context, interval);
}

//
int protocol::read_context(u8 len, const u8 *context, u32 timeout) {
    int s_rc, ret;

    u32 tmo = plc_rte->timestamp() + timeout;
    ret = 0;
    while (len && plc_rte->timestamp() < tmo) {
        s_rc = plc_rte->SerialAvailable(serialNo);
        if (s_rc) {
            *rx_buf.winx = (u8) plc_rte->SerialRead(serialNo);
            rx_buf.winx++;
            len--;
            ret++;
        } else
            plc_rte->delay(1);
    }

    return ret; //
}

int protocol::elt_read_context(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    return read_context(elt->len, nullptr, interval);
}

int protocol::elt_read_len(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    int ret = read_context(elt->len, nullptr, interval);
    if (ret > 0) {
        prl->data_len = *(rx_buf.winx - 1);
    }
    return ret;
}

int protocol::elt_read_data(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    if (!prl->data_len)
        return 0;
    return read_context(prl->data_len, nullptr, interval);
}

u8 protocol::calc_xor(u8 *buf, u8 len) {
    u8 result;
    result = 0;
    for (int i = 0; i < len; i++)
        result ^= buf[i];
    return result;
}

u8 protocol::calc_checsum(u8 *buf, u8 len) {
    u8 result;
    result = 0;
    for (int i = 0; i < len; i++)
        result += buf[i];
    return result;
}

int protocol::checksum_8(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    auto *ele = (protocol_chksum_t *) elt;
    u8 sum;
    sum = 0;
    for (int i = ele->start_byte; i < buf_data_len(&tx_buf); i++)
        sum += tx_buf.buf[i];
    return buf_data_put(&tx_buf, &sum, 1);
}

int protocol::checksum_8_0xff(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    auto *ele = (protocol_chksum_t *) elt;
    u8 sum;
    sum = 0;
    for (int i = ele->start_byte; i < buf_data_len(&tx_buf); i++)
        sum += tx_buf.buf[i];
    sum = 0xff - sum;
    return buf_data_put(&tx_buf, &sum, 1);
}

int protocol::read_cmp_checksum_8(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    u8 sum;
    auto *ele = (protocol_chksum_t *) elt;
    sum = calc_checsum(rx_buf.buf + ele->start_byte, (u8)buf_data_len(&rx_buf));
    return read_cmp_context(1, &sum, interval);
}

int protocol::read_cmp_checksum_8_0xff(protocol_dev_t *dev, protocol_t *prl, protocol_element_t *elt) {
    u8 len, sum;
    auto *ele = (protocol_chksum_t *) elt;
    len = (u8)buf_data_len(&rx_buf);
    sum = 0xff - calc_checsum(rx_buf.buf + ele->start_byte, len);
    return read_cmp_context(1, &sum, interval);
}

int protocol::protocol_receive(protocol_dev_t *dev, protocol_t *prl) {
    int len;
    len = 0;
    for (int i = 0; i < prl->nrs; i++) {
        protocol_element_t *ele = prl->elts[i];
        int ret = ele->check(dev, prl, ele);
        if (ret < 0)
            return ret;
        len += ret;
    }
    return len;
}

int protocol::protocol_encode(protocol_dev_t *dev, protocol_t *prl, protocol_element_t **elts, u8 elen) {
    u8 len;
    len = 0;
    for (int i = 0; i < elen; i++) {
        protocol_element_t *elt = elts[i];
        len += elt->encode(dev, prl, elt);
    }
    return len;
}

int protocol::protocol_decode(protocol_dev_t *dev, protocol_t *prl, protocol_element_t **elts, u8 elen) {
    u8 len;
    len = 0;
    for (int i = 0; i < elen; i++) {
        protocol_element_t *elt = elts[i];
        if (elt->decode)
            elt->decode(dev, prl, elt, rx_buf.buf + len);
        len += elt->len;
    }
    return 0;
}

//! @brief send one protocol
int protocol::protocol_send(protocol_dev_t *dev, protocol_t *prl) {
    u8 len;
    buf_data_clear(&rx_buf);
    buf_data_clear(&tx_buf);
    len = (u8)dev->encode(dev, prl, prl->elts, prl->nrs);
    if (dev->test_cb)
        dev->test_cb(dev, prl);
    return plc_rte->SerialWrite(serialNo,tx_buf.buf, len);
}

int protocol::protocol_send_set(protocol_dev_t *dev, protocol_t *prl) {
    for (int i = 0; i < dev->write_nrs; i++) {
        if (dev->write_array[i] == prl) {
            dev->send_flag |= (1 << i);
            return i;
        }
    }
    return -1;
}

///!function
int protocol::protocol_run(protocol_dev_t *dev, protocol_t *prl) {
    int ret;
    dev->lastsend = prl;
    ret = protocol_send(dev, prl);
    if (prl->rsp) {
        ret = dev->receive(dev, prl->rsp);
        if (ret < 0)
            dev->error = ret;
        else {
            return dev->decode(dev, prl, prl->rsp->elts, prl->rsp->nrs);
        }
    }
    return ret;
}

/**  �豸 Э����ѯ*/
int protocol::protocol_poll(protocol_dev_t *dev) {
    int ret;
    u32 f;
    if (!COMM_STATUS(status, COMM_STATUS_SERIAL_OPEN)) {
        return -1;
    }
    ret = -2;
    if (dev->send_flag) {
        for (int i = 0; i < dev->write_nrs; i++) {
            f = 1 << i;
            if (dev->send_flag & f) {
                ret = protocol_run(dev, dev->write_array[i]);
                if (ret >= 0)
                    dev->send_flag &= (~f);
                else
                    dev->error = ret;
                break;
            }
        }
    } else if (dev->sender) {
        protocol_run(dev, dev->sender);
    } else if (dev->tx) {
        plc_rte->SerialWrite(serialNo, dev->tx->buf, buf_data_len(dev->tx));

    }
    // else if(COMM_STATUS(flag,COMM_STATUS_IDLE))
    {
        while (dev->query_nrs > 0) {
            ret = protocol_run(dev, dev->query_array[dev->next]);
            if (ret < 0)
                dev->error = ret;
            dev->next++;
            if (dev->next >= dev->query_nrs) {
                dev->next = 0;
                break;
            }
        }
    }
    if (dev->rx) {
        dev->receive(dev, dev->rx);
    }
    return ret;
}

protocol_dev_t *protocol_dev_init(protocol_dev_t *dev) {
    memset(dev, 0, sizeof(protocol_dev_t));
    return dev;
}

buf_data *buf_data_init(buf_data *dat, u8 *buf, u16 size) {
    dat->buf = buf;
    dat->rinx = buf;
    dat->winx = buf;
    dat->size = size;
    return dat;
}

void buf_data_clear(buf_data *dat) {
    dat->rinx = dat->buf;
    dat->winx = dat->buf;
}

int buf_data_len(buf_data *dat) {
    if (dat->winx >= dat->rinx)
        return dat->winx - dat->rinx;
    return dat->size - (dat->rinx - dat->winx);
}

int buf_data_read(buf_data *dat, u8 *buf, u16 len) {
    auto l = (u16)buf_data_len(dat);
    if (len > l)
        len = l;
    if (!buf) {
        dat->rinx += len;
        if (dat->rinx > dat->buf + dat->size)
            dat->rinx -= dat->size;
    } else {
        for (l = 0; l < len; l++) {
            buf[l] = *dat->rinx++;
            if (dat->rinx == dat->buf + dat->size)
                dat->rinx = dat->buf;
        }
    }
    return len;
}

int buf_data_put(buf_data *dat, const u8 *buf, u8 nbyte) {
    for (int i = 0; i < nbyte; i++) {
        *dat->winx = *buf;
        dat->winx++;
        if (dat->winx == dat->buf + dat->size)
            dat->winx = dat->buf;
        buf++;
    }
    return nbyte;
}

int buf_data_put_char(buf_data *dat, u8 buf) {
    *dat->winx = buf;
    dat->winx++;
    if (dat->winx == dat->buf + dat->size)
        dat->winx = dat->buf;
    return 1;
}
